package org.ala.tiktools.sharddb.mybatis.pager.without_order;

import org.ala.tiktools.mybatis.pager.Pagement;
import org.ala.tiktools.mybatis.pager.Pager;
import org.ala.tiktools.sharddb.mybatis.executor.ShardExecutor;
import org.ala.tiktools.sharddb.mybatis.executor.ShardNoTxExecutor;
import org.ala.tiktools.sharddb.mybatis.interceptor.Shard;
import org.ala.tiktools.sharddb.mybatis.pager.parameter.ShardPagerParameter;
import org.ala.tiktools.sharddb.mybatis.router.ShardInfo;
import org.ala.tiktools.tools.kv.KV;
import org.ala.tiktools.tools.kv.Maps;

import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


/**
 *  不含OrderBy的处理
 *
 *  @author ala
 *  @date 2025-03-03 15:40
 */
public class ShardPagerWithoutOrderByHandler {


    /**
     *  不含orderBy的查询
     */
    public Map<ShardInfo, ShardExecutor.Result> doPageWithoutOrderBy(ShardPagerParameter parameter) {
        //  取pager参数
        Pager pager = parameter.getPager();

        //  查询总数
        Map<ShardInfo, ShardExecutor.Result> totalResults = ShardNoTxExecutor.doInvoke(parameter.getAnno(), parameter.getThreadPool(), parameter.getCountMethod().getArgs(), parameter.getCountMethod().getProxy(), parameter.getCountMethod().getMethod());
        int total = totalResults.values().stream().mapToInt(e -> ((Integer)e.getRes()).intValue()).sum();
        parameter.setTotal(total);
        //  要查的数据在total中的区域
        int left = pager.getOffset() + 1, right = left + pager.getSize() - 1;
        if (left > total) {
            return parameter.getCountMethod().getArgs().entrySet().stream().collect(Collectors.toMap(
                    e -> e.getKey(),
                    e -> ShardExecutor.Result.success(e.getKey(), Pager.build(pager.getNo(), pager.getSize()).total(total).datas(new ArrayList<>())),
                    (o, n) -> n
            ));
        }

        //  每个分表逐个查数据
        Map<ShardInfo, Pager> pagers = shardPager(totalResults, left, right, pager);

        //  如果没有匹配到的区域，直接返回空数据
        if (pagers.isEmpty()) {
            ShardExecutor.Result empty = ShardExecutor.Result.build(ShardInfo.EMPTY, true, null, Pager.build(pager.getNo(), pager.getSize()).datas(new ArrayList()).total(total), null, 0);
            return Maps.build(KV.build(new ShardInfo(), empty));
        }
        //  如果存在匹配则每个分表查一遍数据，这里直接做汇总
        else {
            Map<ShardInfo, ShardExecutor.Result> results = doQuery(parameter.getAnno(), parameter.getThreadPool(), parameter.getDataMethod().getArgs(), pagers, parameter.getPagerIdx(), parameter.getDataMethod().getProxy(), parameter.getDataMethod().getMethod());
            Pager res = Pager.build(pager.getNo(), pager.getSize());
            res.setTotal(total);
            res.setDatas(new ArrayList());
            pagers.forEach(((shardInfo, _p) -> {
                Pager p = (Pager) results.get(shardInfo).getRes();
                res.getDatas().addAll(p.getDatas());
            }));
            return Maps.build(KV.build(ShardInfo.EMPTY, ShardExecutor.Result.success(ShardInfo.EMPTY, res)));
        }
    }


    /**
     *  将每个分表的数据量处理成本次分页区域，对应与每个分表的Pager
     */
    protected Map<ShardInfo, Pager> shardPager(Map<ShardInfo, ShardExecutor.Result> totalResults, int rangeL, int rangeR, Pager pager) {
        int i = 1;
        Map<ShardInfo, Pager> pagers = new LinkedHashMap<>();

        //  按库名表名的字母序排序
        List<Map.Entry<ShardInfo, ShardExecutor.Result>> shards = totalResults.entrySet().stream().sorted((e1, e2) -> {
            ShardInfo s1 = e1.getKey(), s2 = e2.getKey();
            if (s1.getShardDB().equals(s2.getShardDB())) {
                return s1.getShardTable().compareTo(s2.getShardTable());
            }
            return s1.getShardDB().compareTo(s2.getShardDB());
        }).collect(Collectors.toList());
        //  排序后再遍历
        for (Map.Entry<ShardInfo, ShardExecutor.Result> e : shards) {
            ShardInfo shardInfo = e.getKey();
            int count = (Integer)e.getValue().getRes();
            //  本轮查询在total中的范围
            int _l = i, _r = _l + count - 1;

            //  如果存在交集，取数
            int[] it = intersection(_l, _r, rangeL, rangeR);
            if (it != null) {
                pagers.put(shardInfo, Pager.build(pager.getNo(), pager.getSize()).offset(it[0]).limit(it[1]).queryTotal(false));
            }
            i += count;
        }
        return pagers;
    }
    /**
     *  求两个区域的交集部分，相对于第一个区域的起始下标，长度
     */
    protected int[] intersection(int l1, int r1, int l2, int r2) {
        if ((l2 >= l1 && l2 <= r1) || (l1 >= l2 && l1 <= r2)) {
            int l = Math.max(l1, l2), r = Math.min(r1, r2);
            return new int[] {Math.max(l - l1, 0), r - l + 1};
        }
        return null;
    }


    /**
     *  根据每个分表的Pager，执行分表查询
     */
    protected Map<ShardInfo, ShardExecutor.Result> doQuery(Shard anno, ThreadPoolExecutor threadPool, Map<ShardInfo, Object[]> shardInfoMap, Map<ShardInfo, Pager> pagers, int pageIdx, Object proxy, Method method) {
        Map<ShardInfo, Object[]> args = new HashMap<>();
        pagers.forEach((shardInfo, pager) -> {
            Object[] _args = shardInfoMap.get(shardInfo);
            _args[pageIdx] = pager;
            args.put(shardInfo, _args);
        });
        return ShardNoTxExecutor.doInvoke(anno, threadPool, args, proxy, method);
    }
}
